var default_options = {
    scrolled: false,
    header: true, // Headers visibility
    checked: false, // Checkbox in object row
//    visible: true, // Objectlist visibility
    multiselected: true, // Ability to select the several objects
    columns: [], // Objectlist columns
    width: 'auto', // Objectlist width
    height: 'auto',
    initialData: [], // Initial objects (associative or simple array)
    links: {}, // Cell links
    selectAllCheckbox: false
};

(function($){
	$.fn.objectlist = function(method){
		var args = arguments;
		var results = new Array();
		
		this.each(function() {
			if ( typeof method === 'object' || ! method )
			{
				var options = typeof method !== 'undefined' ? method: {};
				new NewObjectList($(this), options);
			}
			else
			{
				var objectlist = $(this).data('_objectlist');
				
				if ( typeof method === 'undefined' )
					$.error('Element is not an ObjectList.');
				else if ( ! (method in objectlist) )
					$.error(['Method "', method, '" doesn\'t exist.'].join(''));
				else
				{
					var result = objectlist[method].apply(objectlist, Array.prototype.slice.call(args, 1));
					results.push(result);
				}
			}
		});
		
		if ( results.length == 1 )
			return results[0]
		else if ( results.length > 1 )
			return results
		else
			return this
	};
})(jQuery);

// Convert plain list of column properties to object
function Column(objectlist, properties) {
	var self = this;
	
	self.init = function(objectlist, properties) {
		self.title = properties[0];
		self.id = properties[1];
		self.width = properties[2];
		
		self.objectlist = objectlist;
		
		self.widget = null;
	};
	
	
	// Adjust width of TH element
	self._adjust = function(width) {
		if ( self.widget )
			self.widget.width(width);
	};
    
    self.init(objectlist, properties);
};

function NewObjectList(widget, options) {
    var self = this;
    
    // Objectlist initialization
    self.init = function(widget, options) {
        self.widget = $(widget); // Parent div
        
        self.widget.data('_objectlist', self);
        
        self.proxies = new Array(); // Proxy objects
        self.cols = new Array();
        
        self._counter = 0;
		
        self.events = {
        	'select': function(proxy) {}};

        self._setOptions();
        self._render();
    };
	
    // Setting objectlist options
    self._setOptions = function() {
        // Copy options to ObjectList attributes
        for ( attr in default_options )
        {
            if ( ! ( attr in options ) )
                self[attr] = default_options[attr];
            else
                self[attr] = options[attr];
        }
        
        for ( name in self.events )
        	if ( name in options )
        		self.events[name] = options[name];
        
        // Convert columns properties arrays to dictionaries
        // column[0] -> column.title
        // column[1] -> column.id
        // column[2] -> column.width
        for ( i in self.columns )                
        {                                        
            var column = new Column(self, self.columns[i]);
            self.cols[i] = column;
        }
    };

    // ObjectList widget rendering
    self._render = function() {
        self.widget.height(self.height);
        self.widget.css({'display': 'inline'});
        self.table = $('<table/>', {
            'class': 'objectlist',
            'width': self.width});


        if ( self.scrolled )
        {
            var height = self.widget.height()
            if ( self.header )
                height -= 22; // Height for ObjectList header

            // Add scrollable container
            self.container = $('<div/>').appendTo(self.widget);
            self.container.css({
                'height': height,
                'overflow-y':  'scroll',
				'overflow-x':  'hidden',
				'display': 'inline-block'});
            self.table.appendTo(self.container);

            // Add new table for header
            if ( self.header )
            {
                self.htable = self.table.clone() 
                   .prependTo(self.widget);
                self.container.css({
					'border-left': '1px solid #cccccc',
					'border-bottom': '1px solid #cccccc'});
            }
			
			self.table.css({
				'border-left': 'none',
				'border-right': 'none'});
        }
        else
        {
            self.table.appendTo(self.widget);
            self.htable = self.table;
        }

        // Objectlist header
        if ( self.header )
        {
            var thead = $('<thead/>').appendTo(self.htable);
            var tr = $('<tr/>').appendTo(thead);

            // Checkbox column
            if ( self.checked )
			{
				var th = $('<th/>', {id: 'check'}).appendTo(tr);
				if ( self.selectAllCheckbox )
					self.input = $('<input/>', {
						type: 'checkbox'})
						.appendTo(th)
						.change(self.on_select_all);
			}
                
            // Headers
            for ( i in self.cols )
            {
                var column = self.cols[i];
                var th = $('<th/>', {
                    id: column.id,
                    text: column.title})
                    .appendTo(tr);
                
                column.widget = th;
            }

            if ( self.scrolled )
            {
                // Add cell to header for scrollbar alignment
                $('<th/>', {id: 'scroll'})
                    .css({
                    	'border-left': 'none',
                    	'width': scrollbarWidth(),
                    	'padding': '0px 0px 0px 0px'})
                    .appendTo(tr);
                self.table.css({'border-top': 'none'});
            }
        }

        self.tbody = $('<tbody/>').appendTo(self.table);
        
        // Filling the objectlist with the initial data
        for ( i in self.initialData )
            self.add(self.initialData[i]);
        
        if ( ! self.size() )
        	self.blank();
     };

    // Insert object to the defined position of list
    self.insert = function(object, position) {
        // Clear blanked ObjectList with empty item
        if ( self.proxies.length == 1 && self.proxies[0].object.id == -1 )
            self.clear();
        
        position = typeof position === 'undefined' ? self.size() : position;
        
        if ( ! ( 'id' in object ) )
        	object.id = self._counter;
        	
        _w = self.table.width();

        // Create a new proxy between object and DOM elements
        var proxy = new NewProxyObject(object, self, position);
        
        self._counter++;
        
        // Adjust if width changed
        if ( _w !== self.table.width() )
        	self.adjust();
        
        return proxy;
    };
    
    // Add object to the end of list
    self.add = function(object) {
    	return self.insert(object);
    };
    
    // Correct ObjectList styles
    self.adjust = function() {
        if ( self.scrolled )
        {
        	if ( self.header )
        	{
        		if ( self.size() )
        		{
        			var tds = $(self.tbody.find('tr')[0]).find('td');
        			if ( self.checked )
        				tds = tds.slice(1);
					// Adjust headers
        			tds.each(function(index, td) {
        				self.cols[index]._adjust($(td).width());
        			});
        		}
        	}
        }
		console.log(self.container, self.htable)
    };
    
    self._update = function(proxy) {
    	for ( var i = proxy._getPosition(); i < self.size(); i++ )
    		self.proxies[i]._update();
    };

    // Select all items
    self.selectAll = function(select) {
    	var select = typeof select === 'undefined' ? true : select;
        for ( i in self.proxies )
        {
        	var proxy = self.proxies[i];
        	proxy.select(select);
        }
    };

    // Deselect all items
    self.deselectAll = function() {
        self.selectAll(false);
    };
    
    // Remove all items
    self.clear = function() {
        self.tbody.find('tr').remove();

        for ( i in self.proxies )
            self.proxies[i]._unregister(false);

        self.proxies.length = 0;
    };

    // Clear ObjectList and add empty line
    // with disabled checkbox
    self.blank = function() {
        object = {id: -1};
        for ( i in self.cols )
        {
            var column = self.cols[i];
            object[column.id] = "1";
        }
        self.clear();
        var proxy = self.add(object);
        proxy.widget.find('input').prop('disabled', 'disabled');
		self.adjust();
    };

    // Return amount of objects in ObjectList
    self.size = function() {
    	return self.proxies.length;
    };
    
    // Select ObjectList item
    self.select = function(object) {
    	for ( var i = 0; i < self.proxies.length; i++ )
    	{
    		var proxy = self.proxies[i];
    		if ( proxy.object == object )
        		proxy.select()
        }
    };

    // Return object related with selected item
    self.getSelected = function() {
        var objects = new Array();
        for ( var i = 0; i < self.size(); i++ )
        {
            var proxy = self.proxies[i];
            if ( proxy.isSelected() )
                objects.push(proxy.object);
        }
        return objects;
    };

    // Return object by index in list
    self.get = function(index) {
        index = parseInt(index);
        
		var len = self.proxies.length;
		
		if ( ! len )
			return null;
		
		// Items from the end of list
		if ( index < 0 )
			index = len + index;
		
		// Index out of list
		if ( ! (0 <= index < len) )
			return null;
		
		return self.proxies[index].object;
    };

	// Return the first object from list
    self.first = function() {
    	return self.get(0);
    };

	// Return the last object from list
    self.last = function() {
    	return self.get(-1);
    };
    
    self.bind = function(event, handler) {
    	self.events[event] = handler;
    };
	
	self.on_select_all = function() {
		var select = self.input.prop('checked');
		self.selectAll(select);
	};
    
    // Call the ObjectList constructor
    self.init(widget, options)
};

// Proxy between object and DOM element
// Object -> ProxyObject -> DOM element (tr)
//
// References:
//
//     object.proxy -> proxy.widget -> DOM
//     DOM.data -> proxy.object -> object
//
function NewProxyObject(object, objectlist, position) {
    var self = this;

    // ProxyObject initialization
    self.init = function(object, objectlist, position) {
        self.object = object;
        self.objectlist = objectlist;
        
        self._register(position);
        self._render();
    };
        
    // Register ProxyObject
    self._register = function(position) {
        self.object.proxy = self;
        
        var len = self.objectlist.size();
        var pos;
        
        if ( position < 0)
        {
        	if ( len )
        		pos = len + position;
        	else
        		pos = 0;
        }
        else if ( position > len )
        	pos = len;
        else
        	pos = position;
        
        self.objectlist.proxies.splice(pos, 0, self);
    };

    // Unregister/delete ProxyObject
    self._unregister = function(blank) {
        var index = self.objectlist.proxies.indexOf(self);
        var blank = typeof blank === 'undefined' ? true: blank;
        self.objectlist.proxies.splice(index, 1);
        
        delete self.object.proxy;
        
        if (  self.objectlist.size() == 0 && blank )
        	self.objectlist.blank();
    };
    
    self._getPosition = function() {
    	return self.objectlist.proxies.indexOf(self);
    };
    
    self._update = function() {
    	self.widget.removeClass('odd even');
    	var className = ( self._getPosition() % 2 ) == 0 ? 'even' : 'odd';
    	self.widget.addClass(className);
    };
    
    // ObjectList item widget rendering
    self._render = function() {
		console.log(self.object.id === -1 ? 'empty' : self.object.id);
		var tr = $('<tr/>', {
        	'class': 'object',
			'data': self});

			if ( self.object.id == -1)
		   		tr.attr('id', 'empty');
			else
				tr.attr('id', 'obj_' + self.object.id);
		
		var pos = self._getPosition();
		
		if ( pos == 0 )
			tr.prependTo(self.objectlist.tbody);
		else if ( pos == self.objectlist.size() - 1 )
			tr.appendTo(self.objectlist.tbody);
		else
		{
			var element = $(self.objectlist.tbody.find('tr').get(pos));
			tr.insertBefore(element);
		}
		// appendTo(self.objectlist.tbody);
		
		// Cell with checkbox
		if ( self.objectlist.checked )
		{
			var td = $('<td/>', {'class': 'check-column'}).appendTo(tr);
			
			if ( self.objectlist.scrolled )
				td.css({'border-left': 'none'});
			
			$('<input/>', {
				type: 'checkbox',
				value: self.object.id,
				name: 'values'})
				.appendTo(td)
				.change(self.on_checked);
		}
		
		// Cells with values of object properties
		for ( i in self.objectlist.cols )
		{
			var column = self.objectlist.cols[i];
			var td = $('<td/>', {
//               text: self.object[column.id], // Necessary to convert object array to dictionary
               width: column.width})
               .appendTo(tr);
			
			// Cell content
			var value = self.object[column.id];
			if ( column.id in self.objectlist.links )
				$('<a/>', {
					text: value,
					href: self.objectlist.links[column.id].replace('{id}', object.id)})
				.appendTo(td);
			else
				td.text(value);
			
			if ( self.objectlist.scrolled )
				if ( ! self.objectlist.checked && i == 0 )
					td.css({'border-left': 'none'});
				else if ( i == self.objectlist.cols.length - 1 )
					td.css({'border-right': 'none'});
		}
		
		self.widget = tr;
		
		self.objectlist._update(self);
    };
    
    // Select ObjectList item
    self.select = function(select) {
    	var select = typeof select === 'undefined' ? true : select;
    	
        if ( ! select )
            self.widget.removeClass('objectlist-tr-selected');
        else
		{
			if ( ! self.objectlist.multiselected )
				self.objectlist.deselectAll();
			
			self.widget.addClass('objectlist-tr-selected');
		}
        
        var checkbox = self.widget.find('input');
        if ( checkbox.prop('checked') != select )
        	checkbox.prop('checked', select);
    };

    // ObjectList item is selected
    self.isSelected = function() {
        return self.widget.find('input').is(':checked');
    };
    
    // Object selection event
    self.on_checked = function() {
    	self.select($(this).is(':checked'));
    	self.objectlist.events['select'](self);
    };

    // Remove objectlist item
    self.del = function() {
    	self.widget.remove();
    	self._unregister();
    	
    };

    self.init(object, objectlist, position);
};

// Return scrollbar width
function scrollbarWidth() {
    var $inner = jQuery('<div style="width: 100%; height:200px;">test</div>'),
        $outer = jQuery('<div style="width:200px;height:150px; position: absolute; top: 0; left: 0; visibility: hidden; overflow:hidden;"></div>').append($inner),
        inner = $inner[0],
        outer = $outer[0];
     
    jQuery('body').append(outer);
    var width1 = inner.offsetWidth;
    $outer.css('overflow', 'scroll');
    var width2 = outer.clientWidth;
    $outer.remove();
 
    return (width1 - width2);
};